package se.mbaeumer.fxtodo.core.util.dataexport;

import java.io.FileOutputStream;
import java.util.List;

import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.EndDocument;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartDocument;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import se.mbaeumer.fxtodo.core.main.FXTodoMain;
import se.mbaeumer.fxtodo.core.model.Context;
import se.mbaeumer.fxtodo.core.model.ICSInfo;
import se.mbaeumer.fxtodo.core.model.Task;
import se.mbaeumer.fxtodo.core.util.constants.ContextConstants;
import se.mbaeumer.fxtodo.core.util.constants.FilterConstants;
import se.mbaeumer.fxtodo.core.util.constants.ValueConstants;

public class XMLWriter {
	private XMLOutputFactory xmlOutputFactory;
	private XMLEventWriter xmlEventWriter;
	private XMLEventFactory xmlEventFactory;
	private XMLEvent xmlEndLine;
	private XMLEvent xmlTab;
	private StartDocument startDoc;
	private EndDocument endDoc;
	private String configFile = null;
	private List<Context> contexts;
	private List<Task> tasks;
	private List<ICSInfo> icsInfos;
	private FXTodoMain fxtodoMain;
	
	public XMLWriter(FXTodoMain fxtodoMain, String filename) throws Exception{
		this.fxtodoMain = fxtodoMain;				
		this.configFile = filename;
		this.init();
	}

	private void init() throws Exception{
		if (!this.configFile.endsWith(".xml")){
			this.configFile += ".xml";
		}
		this.xmlOutputFactory = XMLOutputFactory.newInstance();
		this.xmlEventWriter = this.xmlOutputFactory.createXMLEventWriter(new FileOutputStream(configFile));
		this.xmlEventFactory = XMLEventFactory.newInstance();
		this.xmlEndLine = this.xmlEventFactory.createDTD("\n");
		this.xmlTab = this.xmlEventFactory.createDTD("\t");
		
		this.startDoc = this.xmlEventFactory.createStartDocument();
		this.endDoc = this.xmlEventFactory.createEndDocument();
		
		this.getDataForExport();
	}
	
	public void writeDataToFile() throws Exception{
		// add start document
		this.xmlEventWriter.add(this.startDoc);
		this.xmlEventWriter.add(this.xmlEndLine);
		
		StartElement rootStartElement = this.xmlEventFactory.createStartElement("",
				"", "fxtodo-data");
		this.xmlEventWriter.add(rootStartElement);
		this.xmlEventWriter.add(this.xmlEndLine);

		this.writeContextsRootTag();
		this.writeTaskRootTag();
		this.writeICSInfoRootTag();
		
		EndElement rootEndElement = this.xmlEventFactory.createEndElement("",
				"", "fxtodo-data");
		this.xmlEventWriter.add(rootEndElement);
		this.xmlEventWriter.add(this.endDoc);
		this.xmlEventWriter.close();
	}
	
	private void writeContextsRootTag() throws XMLStreamException{
		this.xmlEventWriter.add(this.xmlTab);
		StartElement rootStartElement = this.xmlEventFactory.createStartElement("",
				"", "contexts");
		this.xmlEventWriter.add(rootStartElement);
		this.xmlEventWriter.add(this.xmlEndLine);
		
		this.writeContextData();
		
		this.xmlEventWriter.add(this.xmlTab);
		EndElement rootEndElement = this.xmlEventFactory.createEndElement("",
				"", "contexts");
		this.xmlEventWriter.add(rootEndElement);
		this.xmlEventWriter.add(this.xmlEndLine);
	}
	
	private void writeContextData() throws XMLStreamException {
		// go through all contexts
		for (Context c : this.contexts){
			this.xmlEventWriter.add(this.xmlTab);
			this.xmlEventWriter.add(this.xmlTab);
			//this.xmlEventFactory.createAttribute(arg0, arg1, arg2, arg3)
			StartElement rootStartElement = this.xmlEventFactory.createStartElement("",
					"", "context");
			
			this.xmlEventWriter.add(rootStartElement);
			
			this.writeContextAttributes(c);
			EndElement rootEndElement = this.xmlEventFactory.createEndElement("",
					"", "context");
			this.xmlEventWriter.add(rootEndElement);
			this.xmlEventWriter.add(this.xmlEndLine);
		}
	}
	
	private void writeContextAttributes(Context c) throws XMLStreamException{
		XMLEvent attribute = this.xmlEventFactory.createAttribute("id", new Integer(c.getId()).toString());
		this.xmlEventWriter.add(attribute);
		if (c.getTitle().equalsIgnoreCase(ValueConstants.VALUE_NEW)
				|| c.getTitle().equalsIgnoreCase(ValueConstants.VALUE_N_A)){
			c.setTitle("");
		}
		attribute = this.xmlEventFactory.createAttribute("title", c.getTitle());
		this.xmlEventWriter.add(attribute);
		
		if (c.getDescription().equalsIgnoreCase(ValueConstants.VALUE_NEW)
				|| c.getDescription().equalsIgnoreCase(ValueConstants.VALUE_N_A)){
			c.setDescription("");
		}
		attribute = this.xmlEventFactory.createAttribute("description", c.getDescription());
		this.xmlEventWriter.add(attribute);
		attribute = this.xmlEventFactory.createAttribute("created", c.getCreated().toString());
		this.xmlEventWriter.add(attribute);
	}
	
	private void writeTaskRootTag() throws XMLStreamException{
		this.xmlEventWriter.add(this.xmlTab);
		StartElement rootStartElement = this.xmlEventFactory.createStartElement("",
				"", "tasks");
		this.xmlEventWriter.add(rootStartElement);
		this.xmlEventWriter.add(this.xmlEndLine);
		
		this.writeTaskData();
		
		this.xmlEventWriter.add(this.xmlTab);
		EndElement rootEndElement = this.xmlEventFactory.createEndElement("",
				"", "tasks");
		this.xmlEventWriter.add(rootEndElement);
		this.xmlEventWriter.add(this.xmlEndLine);
	}
	
	private void writeTaskData() throws XMLStreamException{
		for (Task t : this.tasks){
			this.xmlEventWriter.add(this.xmlTab);
			this.xmlEventWriter.add(this.xmlTab);
			StartElement rootStartElement = this.xmlEventFactory.createStartElement("",
					"", "task");
			
			this.xmlEventWriter.add(rootStartElement);
			
			this.writeTaskAttributes(t);
			
			EndElement rootEndElement = this.xmlEventFactory.createEndElement("",
					"", "task");
			this.xmlEventWriter.add(rootEndElement);
			this.xmlEventWriter.add(this.xmlEndLine);
		}
	}
	
	private void writeTaskAttributes(Task t) throws XMLStreamException{
		XMLEvent attribute = this.xmlEventFactory.createAttribute("id", new Integer(t.getId()).toString());
		this.xmlEventWriter.add(attribute);
		
		if (t.getTitle().equalsIgnoreCase(ValueConstants.VALUE_NEW)
				|| t.getTitle().equalsIgnoreCase(ValueConstants.VALUE_N_A)){
			t.setTitle("");
		}
		attribute = this.xmlEventFactory.createAttribute("title", t.getTitle());
		this.xmlEventWriter.add(attribute);
		
		if (t.getDescription().equalsIgnoreCase(ValueConstants.VALUE_NEW)
				|| t.getDescription().equalsIgnoreCase(ValueConstants.VALUE_N_A)){
			t.setDescription("");
		}
		attribute = this.xmlEventFactory.createAttribute("description", t.getDescription());
		this.xmlEventWriter.add(attribute);
		attribute = this.xmlEventFactory.createAttribute("created", t.getCreated().toString());
		this.xmlEventWriter.add(attribute);
		attribute = this.xmlEventFactory.createAttribute("dueDate", t.getDueDateString());
		this.xmlEventWriter.add(attribute);
		String contextId = null;
		
		if (t.getTaskContext() != null){
			Integer i = new Integer(t.getTaskContext().getId());
			contextId = i.toString(); 
		}
		
		if (contextId == null){
			contextId="";
		}
		attribute = this.xmlEventFactory.createAttribute("contextId", contextId);
		this.xmlEventWriter.add(attribute);
		
		attribute = this.xmlEventFactory.createAttribute("status", t.getStatus().toString());
		this.xmlEventWriter.add(attribute);
		attribute = this.xmlEventFactory.createAttribute("priority", t.getPriority().toString());
		this.xmlEventWriter.add(attribute);
	}
	
	private void writeICSInfoRootTag() throws XMLStreamException{
		this.xmlEventWriter.add(this.xmlTab);
		StartElement rootStartElement = this.xmlEventFactory.createStartElement("",
				"", "icsinfos");
		this.xmlEventWriter.add(rootStartElement);
		this.xmlEventWriter.add(this.xmlEndLine);
		
		this.writeICSInfoData();
		
		this.xmlEventWriter.add(this.xmlTab);
		EndElement rootEndElement = this.xmlEventFactory.createEndElement("",
				"", "icsinfos");
		this.xmlEventWriter.add(rootEndElement);
		this.xmlEventWriter.add(this.xmlEndLine);
	}
	
	private void writeICSInfoData() throws XMLStreamException{
		for (ICSInfo ics : this.icsInfos){
			this.xmlEventWriter.add(this.xmlTab);
			this.xmlEventWriter.add(this.xmlTab);
			StartElement rootStartElement = this.xmlEventFactory.createStartElement("",
					"", "icsinfo");
			
			this.xmlEventWriter.add(rootStartElement);
			
			this.writeICSInfoAttributes(ics);
			
			EndElement rootEndElement = this.xmlEventFactory.createEndElement("",
					"", "icsinfo");
			this.xmlEventWriter.add(rootEndElement);
			this.xmlEventWriter.add(this.xmlEndLine);
		}
	}
	
	private void writeICSInfoAttributes(ICSInfo ics) throws XMLStreamException{
		// id
		XMLEvent attribute = this.xmlEventFactory.createAttribute("id", new Integer(ics.getId()).toString());
		this.xmlEventWriter.add(attribute);
		
		// summmary
		if (ics.getsummary().equalsIgnoreCase(ValueConstants.VALUE_NEW)
				|| ics.getsummary().equalsIgnoreCase(ValueConstants.VALUE_N_A)){
			ics.setSummary("");
		}
		attribute = this.xmlEventFactory.createAttribute("summary", ics.getsummary());
		this.xmlEventWriter.add(attribute);
		
		// description
		if (ics.getDescription().equalsIgnoreCase(ValueConstants.VALUE_NEW)
				|| ics.getDescription().equalsIgnoreCase(ValueConstants.VALUE_N_A)){
			ics.setDescription("");
		}
		attribute = this.xmlEventFactory.createAttribute("description", ics.getDescription());
		this.xmlEventWriter.add(attribute);
		
		// created
		attribute = this.xmlEventFactory.createAttribute("created", ics.getCreatedString());
		this.xmlEventWriter.add(attribute);
		
		// start time
		attribute = this.xmlEventFactory.createAttribute("startTime", ics.getStartTimeString());
		this.xmlEventWriter.add(attribute);
		
		// end time
		if (ics.getEndTime() == null){
			attribute = this.xmlEventFactory.createAttribute("endTime", "");
			this.xmlEventWriter.add(attribute);
		}else{
			attribute = this.xmlEventFactory.createAttribute("endTime", ics.getEndTimeString());
			this.xmlEventWriter.add(attribute);
		}
		
		// priorityid INTEGER NOT NULL,
		attribute = this.xmlEventFactory.createAttribute("priority", ics.getPriority().toString());
		this.xmlEventWriter.add(attribute);
		
		// statusid INTEGER NOT NULL,
		attribute = this.xmlEventFactory.createAttribute("status", ics.getStatus().toString());
		this.xmlEventWriter.add(attribute);
		
		// typeid INTEGER NOT NULL,
		attribute = this.xmlEventFactory.createAttribute("type", ics.getICSInfoType().toString());
		this.xmlEventWriter.add(attribute);
		
		// taskid INTEGER NOT NULL,
		attribute = this.xmlEventFactory.createAttribute("taskId", new Integer(ics.getTaskId()).toString());
		this.xmlEventWriter.add(attribute);
	}
	
	private void getDataForExport(){
		this.getContexts();
		this.getTasks();
		this.getICSInfo();
	}
		
	private void getContexts(){
		this.contexts = fxtodoMain.getAllContexts();
	}
	
	private void getTasks(){
		String contextTitle = ContextConstants.CONTEXT_ALL;
		String period = FilterConstants.FILTER_NONE;
		String status = FilterConstants.FILTER_NONE;
		String priority = FilterConstants.FILTER_NONE;
		this.tasks = this.fxtodoMain.getTasks(contextTitle, period, status, priority);
	}
	
	private void getICSInfo(){
		this.icsInfos = this.fxtodoMain.getICSInfos();
	}
	
}
